package website.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import website.constants.Code;
import website.domain.*;
import website.mapper.ClassifyMapper;
import website.mapper.ResMapper;
import website.mapper.TagMapper;
import website.service.CommentService;
import website.service.ResService;
import website.service.WebService;
import website.utils.NowDateUtils;

import java.time.Duration;
import java.util.List;

import static website.constants.RedisConstants.*;


@Service
public class ResServiceImpl extends ServiceImpl<ResMapper, Resource> implements ResService {

    @javax.annotation.Resource
    private ClassifyMapper classifyMapper;

    @javax.annotation.Resource
    private TagMapper tagMapper;

    @Autowired
    private CommentService commentService;

    @Autowired
    private WebService webService;

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 查询分类
     * @return
     */
    @Override
    public ResModel<List<Classify>> getClassify() {
        //更新网站浏览次数
        webService.update().setSql("visits = visits +1").update();
        List<Classify> classifyList = classifyMapper.selectList(null);
        return ResModel.success(Code.SUCCESS,classifyList);
    }

    /**
     * 分页分类查询资源
     * @param current
     * @param size
     * @param id
     * @return
     */
    @Override
    public ResModel<IPage<Resource>> getByPage(Integer current, Integer size, Integer id) {
        Page<Resource> page = new Page<>(current, size);
        Classify classify = classifyMapper.selectById(id);
        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(classify.getClassifyName()!=null,Resource::getClassify,classify.getClassifyName());
        Page<Resource> res = page(page, wrapper);
        List<Resource> resourceList = res.getRecords();
        for (Resource resource : resourceList) {
            ResModel rm = commentService.getCount(resource.getId());
            resource.setComments((Integer) rm.getData());
        }
        //更新网站浏览次数
        webService.update().setSql("visits = visits +1").update();
        return ResModel.success(Code.SUCCESS,res);
    }

    /**
     * 查询分类名称
     * @param id
     * @return
     */
    @Override
    public ResModel getClassifyName(Integer id) {
        Classify classify = classifyMapper.selectById(id);
        return ResModel.success(Code.SUCCESS,classify.getClassifyName());
    }

    /**
     * 查询资源详情
     * 缓存空值解决 缓存穿透
     * 互斥锁解决 缓存击穿
     * @param id
     * @return
     */
    @Override
    public ResModel<Resource> getResource(Integer id) {
        String key = CACHE_RESOURCE_KEY + id;
        //查询缓存
        String json = redisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)){//缓存存在
            Resource resource = JSONUtil.toBean(json,Resource.class);
            return ResModel.success(Code.SUCCESS,resource);
        }
        //命中空值
        if (json!=null){
            return ResModel.error(Code.NON_EXISTENT);
        }
        String lockKey=MUTEX_RESOURCE_KEY+id;
        Resource resource = null;
        try {
            //尝试获得锁
            boolean b = tryLock(lockKey);
            //没有获得锁，重试
            if (!b){
                return getResource(id);
            }
            //缓存不存在，查询数据库
            resource = getById(id);
            if (resource==null){
                // 缓存空值解决 缓存穿透
                redisTemplate.opsForValue().set(key,"",Duration.ofMinutes(2));
                return ResModel.error(Code.NON_EXISTENT);
            }
            //添加缓存
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(resource), Duration.ofMinutes(30));
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //释放锁
            unlock(lockKey);
        }
        return ResModel.success(Code.SUCCESS,resource);
    }

    /**
     * 获得锁
     * @param key
     * @return
     */
    private boolean tryLock(String key) {
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", Duration.ofSeconds(10));
        return BooleanUtil.isTrue(flag);
    }

    /**
     * 释放锁
     * @param key
     */
    private void unlock(String key) {
        redisTemplate.delete(key);
    }
    /**
     * 分页查询分类名称
     * @param current
     * @param size
     * @return
     */
    @Override
    public ResModel<IPage<Classify>> getByClassifyPage(Integer current, Integer size) {
        Page<Classify> page = new Page<>(current, size);
        Page<Classify> messagePage = classifyMapper.selectPage(page, null);
        return ResModel.success(Code.SUCCESS,messagePage);
    }

    /**
     * 删除分类名称
     * @param id
     * @return
     */
    @Override
    public ResModel delClassify(Integer id) {
        if (id!=null){
            int i = classifyMapper.deleteById(id);
            return ResModel.success(Code.DELETE,i);
        }
        return ResModel.error(Code.FAIL);
    }

    /**
     * 添加分类名称
     * @param classify
     * @return
     */
    @Override
    public ResModel addClassify(Classify classify) {
        if (classify.getClassifyName()!=null){
            int insert = classifyMapper.insert(classify);
            redisTemplate.delete(CACHE_CLASSIFY_KEY);
            return ResModel.success(Code.SUCCESS,insert);
        }
        return ResModel.error(Code.FAIL);
    }

    /**
     * 删除资源信息
     * @param id
     * @return
     */
    @Override
    public ResModel delRes(Integer id) {
        if (id!=null){
            boolean i = removeById(id);
            return ResModel.success(Code.DELETE,i);
        }
        return ResModel.error(Code.FAIL);
    }

    /**
     * 更新资源信息
     * @param resource
     * @return
     */
    @Transactional
    public ResModel updateRes(Resource resource) {
        Integer id = resource.getId();
        if (id==null){
            return ResModel.error(Code.FAIL);
        }
        updateById(resource);
        redisTemplate.delete(CACHE_RESOURCE_KEY+id);
        return ResModel.success(Code.UPDATE,resource);
    }

    /**
     * 添加资源信息
     * @param resource
     * @return
     */
    @Override
    public ResModel addRes(Resource resource) {
        resource.setIssueTime(NowDateUtils.getNowDate());
        String[] values = resource.getValues();
        String join = StrUtil.join(",", values);
        resource.setTag(join);
        boolean save = save(resource);
        ResModel<WebInfo> webInfo = webService.getWebInfo();
        WebInfo data = webInfo.getData();
        data.setResourceCount(data.getResourceCount()+1);
        boolean b = webService.updateById(data);
        return ResModel.success(Code.INSERT,save);
    }

    /**
     * 更新资源浏览量
     * @param id
     * @return
     */
    @Override
    @Transactional
    public ResModel addResView(Integer id) {
        if (id==null){
            return ResModel.error(Code.NON_EXISTENT);
        }
        //更新资源浏览次数
        update().setSql("views = views + 1").eq(true,"id",id).update();
        //更新网站浏览次数
        webService.update().setSql("visits = visits +1").update();
        return ResModel.success(Code.SUCCESS,id);
    }

    /**
     * 查询标签
     * @return
     */
    @Override
    public ResModel<List<Tag>> listTag() {
        List<Tag> tags = tagMapper.selectList(null);
        return ResModel.success(Code.SUCCESS,tags);
    }

    /**
     * 查询热门资源
     * @return
     */
    @Override
    public ResModel<List<Resource>> getHot() {
        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(true,Resource::getViews).last("limit 0,4");
        List<Resource> list = list(wrapper);
        return ResModel.success(Code.SUCCESS,list);
    }

    /**
     * 查询自己资源
     * @param current
     * @param size
     * @param uid
     * @return
     */
    @Override
    public ResModel<IPage<Resource>> getByUidResList(Integer current, Integer size, Integer uid) {
        Page<Resource> page = new Page<>(current, size);
        LambdaQueryWrapper<Resource> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(true,Resource::getUid,uid);
        Page<Resource> res = page(page, wrapper);
        return ResModel.success(Code.SUCCESS,res);
    }
}
